package qj.tool.config;

import java.util.*;

import qj.tool.config.ConfigOrient.ConfigMeta.Group;
import qj.tool.db.DB;
import qj.util.cache.Caches;
import qj.util.funct.*;

@SuppressWarnings({"unchecked","rawtypes"})
public class ConfigOrient {

	List<Group> configMetaGroups;

//	List<ConfigMeta> configMetas;

	Map<String,F0> getterMap = new HashMap<String,F0>();
	Map<String,F0> convertMap = new HashMap<String,F0>();

	public F0<DB> dbF;

	public String getString(String name) {
		return get(name);
	}
	public String getStringRaw(String name) {
		ConfigMeta meta = getMeta(name);
		return (String) getter_inner(meta.type, meta.defaul).e(name);
	}
	public <A> A getConvertRaw(String name) {
		ConfigMeta meta = getMeta(name);
		for (Douce<String, F1> douce : meta.conversions) {
			Object value = getter_inner(meta.type, meta.defaul).e(name);
			return (A) douce.get2().e(value);
		}
		return null;
	}

	private ConfigMeta getMeta(String name) {
		for (Group group : configMetaGroups) {
			for (ConfigMeta configMeta : group.configMetas) {
				if (configMeta.name.equals(name)) {
					return configMeta;
				}
			}
		}
		return null;
	}
	
	public void setString(String name, String value) {
		dbF.e().setDictionary(name, "config", new Config(name,value));
	}
	
	public void setBoolean(String name, boolean value) {
		dbF.e().setDictionary(name, "config", new Config(name,value));
	}

	public void clear(String name) {
		dbF.e().deleteDictionary(name, "config");
	}

	public <A> A getConvert(String name) {
		return (A) convertMap.get(name).e();
	}
	public <A> A get(String name) {
		return (A) getterMap.get(name).e();
	}
	public <A> F0<A> getF(final String name) {
		return new F0<A>() {public A e() {
			return get(name);
		}};
	}
	public <A> F0<A> getConvertF(final String name) {
		return new F0<A>() {public A e() {
			return getConvert(name);
		}};
	}
	
	public static ConfigMeta meta(String name,String type,Integer refreshRate,String storeType) {
		return new ConfigMeta(name, type, refreshRate, storeType);
	}
	
	public static class ConfigMeta {
		String name;
		String type;
		Integer refreshRate;
		String storeType;
		Object defaul;
		private List<Douce<String,F1>> conversions = new LinkedList<Douce<String,F1>>();
		public ConfigMeta(String name, String type, Integer refreshRate, String storeType) {
			this.name = name;
			this.type = type;
			this.refreshRate = refreshRate;
			this.storeType = storeType;
		}
		public ConfigMeta defaul(Object defaul) {
			this.defaul = defaul;
			return this;
		}
		public ConfigMeta convert(F1 convertF) {
			conversions.add(new Douce<String, F1>(name, convertF));
			return this;
		}
		public ConfigMeta convert(String name, F1 convertF) {
			conversions.add(new Douce<String, F1>(name, convertF));
			return this;
		}

		public static class Group {
			String name;
			List<ConfigMeta> configMetas;
		}
	}

	/**
	 * {name,type,refreshRate,manual/system}
	 * Example:
	 * {"user.name","String",15,"manual"}
	 * @param meta
	 */
	public void init(List<Group> configMetaGroups) {
		this.configMetaGroups = configMetaGroups;
		
		initGetters(configMetaGroups);
	}
	
	private void initGetters(List<Group> configMetaGroups) {
		for (Group group : configMetaGroups) {
			for (ConfigMeta configMeta : group.configMetas) {
				accept(configMeta);
			}
		}
	}
	private void accept(ConfigMeta configMeta) {
		getterMap.put(configMeta.name, 
				Caches.fixM(configMeta.refreshRate,Fs.f0( getter_inner(configMeta.type, configMeta.defaul), configMeta.name)));
		
		for (Douce<String, F1> conversion : configMeta.conversions) {
			String name = conversion.get1();
			F1 convertF = conversion.get2();
			convertMap.put(name, 
					Caches.fixM(configMeta.refreshRate, Fs.f0( Fs.chain(getter_inner(configMeta.type, configMeta.defaul), convertF), configMeta.name)));
		}
	}
	

	
	public void refresh () {
		initGetters(configMetaGroups);
	}
	
	public void refresh(String name) {
		ConfigMeta meta = getMeta(name);
		accept(meta);
	}

	public <A> F1<String,A> getter_inner(String type, final A defaul) {
		final F1<Config, Object> getter1 = getterByType(type);
		return new F1<String, A>() {public A e(String name) {
			Config config = dbF.e().getDictionary(name, "config", Config.class);
			if (config==null) {
				return defaul;
			}
			A val = (A) getter1.e(config);

			if (val==null) {
				return defaul;
			}
			return val;
		}};
	}
	
	private static <A> F1<Config, A> getterByType(String type) {
		if (type==null || "String".equals(type) || "BigString".equals(type)) {
			return new F1<Config,A>() {public A e(Config config) {
				return (A) config.string;
			}};
		} else if ("Boolean".equals(type)) {
			return new F1<Config,A>() {public A e(Config config) {
				return (A) config.bool;
			}};
		} else if ("Date".equals(type)) {
			return new F1<Config,A>() {public A e(Config config) {
				return (A) config.date;
			}};
		} else if ("Integer".equals(type)) {
			return new F1<Config,A>() {public A e(Config config) {
				return (A) config.integer;
			}};
		} else if ("Double".equals(type)) {
			return new F1<Config,A>() {public A e(Config config) {
				return (A) config.doubl;
			}};
		} else if ("Decimal".equals(type)) {
			return new F1<Config,A>() {public A e(Config config) {
				return (A) config.decimal;
			}};
		} else {
			throw new RuntimeException("Unknown type: " + type);
		}
	}

	public static Group group(String name, List<ConfigMeta> configMetas) {
		Group group = new Group();
		group.name = name;
		group.configMetas = configMetas;
		return group;
	}

}
